/**
  ******************************************************************************
  * @file           
  * @author         古么宁
  * @brief          IEC101 链路层解析
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */
/* Includes ---------------------------------------------------*/
#include <string.h>

#include "iec10x.h"
#include "iec10x_link.h"
#include "iec10x_event.h"
#include "iec10x_debug.h"

#include "COT.h"
#include "ASDUTYP.h"

#if (ENABLE_IEC101_MODE)

/* Private types ------------------------------------------------------------*/

/* Private macro ------------------------------------------------------------*/

#define PARSE_OK         0
#define PARSE_ERROR      1
#define PARSE_INCOMPLETE 2

/* Private variables --------------------------------------------------------*/


/* Private function prototypes -----------------------------------------------*/

static void iec101slv_longpkt(iec10x_t * bus ,struct longpkt * recvpkt) ;
static void iec101mst_longpkt(iec10x_t * bus ,struct longpkt * recvpkt) ;

static void unbals_link_check (iec10x_t * bus, uint8_t ) ;
static void unbalm_link_check(iec10x_t * bus, uint8_t ) ;

static void bals_link_check (iec10x_t * bus, uint8_t ) ;
static void balm_link_check(iec10x_t * bus, uint8_t ) ;

//------------------------------------------------------------------------------

static void _iec101_shortpkt(iec10x_t * bus)
{
	iec101_protocol_send(bus,NULL,0,bus->iec101.lnctrl);
	if (bus->mode & (IEC_MASTER|IEC_BALANCE)) {
		if (bus->operate.todo != NULL) {
			bus->iec101.todo = bus->operate.todo;
		}
	}
}


void iec101_shortpkt(iec10x_t * bus ,uint8_t ctrl , iec_fn_t nextfn)
{
	bus->iec101.lnctrl    = ctrl;
	bus->iec101.todo     = _iec101_shortpkt;
	bus->operate.todo = nextfn;
}



/**
  * @brief    从机反馈初始化结束报文
  * @param    bus   : 初始化结束的总线
  * @return   返回回复码
*/
static void  iec101slv_initpkt(iec10x_t * bus)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t * ASDU = ASDU_TXBUFF(bus);
	uint8_t lnkctrl  ;
	int ASDUlen = IEC_INFO + bus->infoaddrlen + 1 ;

	ASDU[IEC_TYPE]          = M_EI_NA_1               ;
	ASDU[IEC_QUALIFIER]     = 0x01                    ; 
	ASDU[IEC_COT]           = COT_INIT                ;
	ASDU[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDU[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDU[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;
	ASDU[IEC_INFO]          = 0                       ;
	ASDU[IEC_INFO+1]        = 0                       ;
	ASDU[IEC_INFO+2]        = 0                       ;
	ASDU[IEC_INFO + bus->infoaddrlen] = 0             ;

	if (bus->mode & IEC_BALANCE) {
		int FCB = (bus->iec101.FCB == 0);
		bus->iec101.FCB = FCB;
		lnkctrl  = IEC_CTRL(0,1,FCB,1,MASTER_TRANS);
		lnkctrl |= (bus->mode & IEC_DIRECT) ;
	}
	else {
		lnkctrl = IEC_CTRL(0,0,0,0,SLAVE_ACK);
	}

	iec101_protocol_send(bus,ASDU,ASDUlen,lnkctrl);

	memset(&bus->operate,0,sizeof(struct iec_do));
}


/**
  * @brief    计算和校验
  * @param    data   : 需要校验的数据 
  * @param    len    : 需要校验的数据长度 
  * @return   返回回复码
*/
static uint8_t calchksum(uint8_t * data , uint8_t len) 
{
	uint32_t sum = 0 ;
	for (int i = 0 ; i < len ; ++i,++data)
		sum += *data;
	return (uint8_t)(sum & 0x00ff);
}


/**
  * @brief    从机不定长包解析
  * @param    bus       : 总线
  * @param    recvpkt   : 接收到的长包
  * @return   don't care
*/
static void iec101slv_longpkt(iec10x_t * bus ,struct longpkt * recvpkt)
{
	uint8_t * ASDU    ;
	int       ASDUlen ;
	uint32_t  addr = recvpkt->linkaddr[0]; 

	if (bus->iec101.lnaddrlen > 1) {
		addr |= (recvpkt->linkaddr[1] << 8) ;
	}

	if (bus->iec101.lnaddr != addr) {
		iec10x_warn(bus,"address does not match.\r\n");
		return ;
	}

	ASDU    = &recvpkt->linkaddr[bus->iec101.lnaddrlen] ;
	ASDUlen = recvpkt->len - 1 - bus->iec101.lnaddrlen  ;

	if (bus->mode & IEC_BALANCE)
		bals_link_check(bus,recvpkt->linkctrl);
	else
		unbals_link_check(bus,recvpkt->linkctrl);

	/* 查找对应类型的响应函数，响应函数仅能通过 iec104_type_listen() 添加，
	   在 iec104_protocol_init() 统一注册 */
	ASDUprofn_t asdupro = ASDU_pro(ASDU[0]) ;

	if ( asdupro != NULL ) {
		if (0){//(bus->waiting_confirm) {
			bus->busy &= ~IEC101_WAITING_CONFORM ;
			memset(&bus->operate,0,sizeof(struct iec_do));
			iec10x_warn(bus,"there are unrecovered packets.\r\n");
		}
		asdupro(bus,ASDU,ASDUlen);
	}
	else {
		iec10x_err(bus,"unknown type(0x%x) packet.\r\n",ASDU[0]);
		iec101_protocol_send(bus,NULL,0,SLVCTRL(SLAVE_OK,0,0));
	}
}

/**
  * @brief    非平衡模式下做从机，解析判断链路层
  * @param    bus           : 需要解析的总线结构体 
  * @param    ctrlfrommaster: 来自主机的控制域
  * @return   don't care
*/
static void unbals_link_check(iec10x_t * bus , uint8_t ctrlfrommaster)
{
	uint8_t ackctrl = 0;
	linkctrl_t linkctrl = {.val = ctrlfrommaster};//提取控制码并分析

	switch ( linkctrl.master.FUNCTION ){
	case MASTER_RST_LNK:  
		/* 从机响应主机的复位链路请求,回复完确认后，
		   主机会召唤数据 ，从机以 iec101slv_initpkt() 回复 */
		ackctrl = SLVCTRL(SLAVE_OK,0,1) ;
		iec101_shortpkt(bus,ackctrl,iec101slv_initpkt);
		break;

	case MASTER_TRANS: //发送/确认用户数据
		break;
		
	case MASTER_TRANS_NOACK: //发送/无应答用户数据
		break ;

	case 8://访问请求
	case MASTER_LNK_STAT://请求链路状态
		ackctrl = SLVCTRL(SLAVE_STATE,0,0) ;
		iec101_shortpkt(bus,ackctrl,NULL);
		break;
	
	case MASTER_DATA_1: //主机请求一级数据
		/* 如果有突发事件，先对突发事件进行发送。发送成功则不进行数据召唤 */
		if (bus->ctrl.todo != NULL) {
			bus->iec101.todo = bus->ctrl.todo ;
		}
		else
		if (bus->event.next != &bus->event) {
			bus->iec101.todo = iec10xslv_event_pro ;
		}
		else
		if (bus->operate.todo != NULL) {
			bus->iec101.todo = bus->operate.todo ;
		}
		else {
			ackctrl = SLVCTRL(SLAVE_NOACK,0,0) ;
			iec101_shortpkt(bus,ackctrl,NULL);
		}
		break;
	
	case MASTER_DATA_2: //二级数据
		/* 如果有突发事件，或者一级数据，置位 ACD ，请求召唤 1 级数据 */
		if (bus->operate.todo != NULL || bus->event.next != &bus->event){
			ackctrl = SLVCTRL(SLAVE_NOACK,0,1) ;
			iec101_shortpkt(bus,ackctrl,bus->operate.todo);
		}
		else{
			ackctrl = SLVCTRL(SLAVE_NOACK,0,0) ;
			iec101_shortpkt(bus,ackctrl,NULL);
		}
		break;

	default: break;
	}
}


/**
  * @brief    平衡模式下做从机，解析判断链路层
  * @param    bus           : 需要解析的总线结构体 
  * @param    ctrlfrommaster: 来自主机的控制域
  * @return   don't care
*/
static void bals_link_check(iec10x_t * bus , uint8_t ctrlfrommaster)
{
	uint8_t ackctrl = 0;
	linkctrl_t linkctrl = {.val = ctrlfrommaster};
	
	if (linkctrl.master.PRM) {
		/* 数据包是主机主导发送的 */
		switch ( linkctrl.master.FUNCTION ){
		//case MASTER_TRANS  :  //发送/确认用户数据
		case MASTER_RST_LNK:  
			/* 从机响应主机的复位链路请求，平衡模式下作为从机时再发送连接请求*/
			ackctrl  = IEC_CTRL(0,0,0,0,SLAVE_OK) ;
			ackctrl |= (bus->mode & IEC_DIRECT) ;
			iec101_shortpkt(bus,ackctrl,iec101mst_conncet);
			break; 
			
		case MASTER_TRANS_NOACK: //发送/无应答用户数据
			break ;

		case MASTER_TEST_LNK:// 从机响应主机的复位链路请求
			ackctrl  = IEC_CTRL(0,0,0,0,SLAVE_OK) ;
			ackctrl |= (bus->mode & IEC_DIRECT) ;
			iec101_shortpkt(bus,ackctrl,NULL);
			break;

		case 8://访问请求
		case MASTER_LNK_STAT://请求链路状态
			ackctrl  = IEC_CTRL(0,0,0,0,SLAVE_STATE) ;
			ackctrl |= (bus->mode & IEC_DIRECT) ;
			iec101_shortpkt(bus,ackctrl,NULL);
			break;

		default: break;
		}
	}
	else {
		/* 接收到来自主机的响应 */
		switch ( linkctrl.slave.FUNCTION ){
		case SLAVE_OK :
			if (bus->connected == IEC_DISCONNECTED) { 
				/* 确认帧为连接链路帧，发送初始化结束包 */
				bus->connected  = IEC_CONNECTED;
				bus->iec101.todo = iec101slv_initpkt;
			}
			//!! 此处没有 break !!//break; 
		case SLAVE_BUSY: 
		case SLAVE_ACK   :
		case SLAVE_NOACK :
			bus->busy &= ~IEC101_WAITING_CONFORM ;
			if (bus->ctrl.todo != NULL) {
				bus->iec101.todo = bus->ctrl.todo ;
			}
			else
			if (&bus->event != bus->event.next) {
				bus->iec101.todo = iec10xmst_event_pro;
			}
			else 
			if (NULL != bus->operate.todo) {
				bus->iec101.todo = bus->operate.todo;
			}
			break;

		case SLAVE_STATE :
			/* 从机发送复位远方链路报文 */
			bus->busy |= IEC101_WAITING_CONFORM ;
			ackctrl  = IEC_CTRL(0,1,0,0,MASTER_RST_LNK);
			ackctrl |= (bus->mode & IEC_DIRECT) ;
			//iec101_shortpkt(bus,ackctrl,NULL);
			iec101_protocol_send(bus,NULL,0,ackctrl);
			break ;
		}
	}
}


/**
  * @brief    主机模式。如果已知固定帧长包包头，尝试解析
  *           在非平衡模式下，从机不会主动发数据，因此主机接收到数据一定是主机
  *           先发送了指令。所以此时的总线状态为忙碌。
  * @param    bus    : 需要解析的总线结构体 
  * @param    datapkt: 固定长度数据包
  * @param    pktlen : 固定长度数据包长度
  * @return   don't care
*/
static void unbalm_link_check(iec10x_t * bus , uint8_t ctrlfromslave)
{
	linkctrl_t linkctrl = {.val = ctrlfromslave};

	if (linkctrl.slave.ACD) {
		int FCB = (bus->iec101.FCB == 0); 
		bus->iec101.FCB = FCB;
		iec101_protocol_send(bus,NULL,0,MSTCTRL(MASTER_DATA_1,1,FCB));
	}
	
	switch (linkctrl.slave.FUNCTION) {
	case SLAVE_OK :
		if (bus->connected == IEC_DISCONNECTED) { 
			/* 确认帧为连接链路帧，连接完成后自动进行总召唤 */
			iec101_connected(bus);
		}
	/* //!! 此处没有 break !! */
	case SLAVE_ACK   :
	case SLAVE_NOACK :
		bus->busy &= ~IEC101_WAITING_CONFORM ; 
		break;

	case SLAVE_STATE :
		/* 复位链路状态 */
		iec101_protocol_send(bus,NULL,0,MSTCTRL(MASTER_RST_LNK,0,0)); 
	break ;
	}
}


/**
  * @brief    平衡模式下做主机，解析判断链路层
  * @param    bus          : 需要解析的总线结构体 
  * @param    ctrlfromslave: 来自从机的控制域
  * @return   don't care
*/
static void balm_link_check(iec10x_t * bus , uint8_t ctrlfromslave)
{
	unsigned char lnctrl ;
	linkctrl_t linkctrl = {.val = ctrlfromslave};

	if (linkctrl.master.PRM) { 
		/* 数据包是从机主导发送的，此处 作出响应 */
		switch ( linkctrl.master.FUNCTION ){
		case MASTER_TRANS  :
		case MASTER_RST_LNK:  
			/* 从机响应主机的复位链路请求 */
			lnctrl  = IEC_CTRL(0,0,0,0,SLAVE_OK);
			lnctrl |= (bus->mode & IEC_DIRECT) ;
			iec101_protocol_send(bus,NULL,0,lnctrl);
			if (bus->connected == IEC_DISCONNECTED) { 
				/* 确认帧为连接链路帧，连接完成后自动进行总召唤 */
				iec101_connected(bus);
			}
			break;
	
		case 8://访问请求
		case MASTER_LNK_STAT://请求链路状态 
			lnctrl  = IEC_CTRL(0,0,0,0,SLAVE_STATE);
			lnctrl |= (bus->mode & IEC_DIRECT) ;
			iec101_protocol_send(bus,NULL,0,lnctrl);
			break;

		default: break;
		}
	}
	else {
		/* 接收到来自从机的响应 */
		switch ( linkctrl.slave.FUNCTION ){
		case SLAVE_OK :
		case SLAVE_BUSY  : 
		case SLAVE_ACK   :
		case SLAVE_NOACK :
			bus->busy &= ~IEC101_WAITING_CONFORM ;
			break;

		case SLAVE_STATE :
			/* 复位链路状态 */
			lnctrl  = IEC_CTRL(0,1,0,0,MASTER_RST_LNK);
			lnctrl |= (bus->mode & IEC_DIRECT) ;
			iec101_protocol_send(bus,NULL,0,lnctrl); 
			break ;
		}
	}
}


/**
  * @brief    主机总线接收不定长数据
  * @param    bus     : 需要解析的总线结构体 
  * @param    recvpkt : 包数据 
  * @return   don't care
*/
static void iec101mst_longpkt(iec10x_t * bus ,struct longpkt * recvpkt)
{
	uint8_t * ASDU    = &recvpkt->linkaddr[bus->iec101.lnaddrlen] ;
	int32_t   ASDUlen = recvpkt->len - 1 - bus->iec101.lnaddrlen ;
	ASDUprofn_t asdupro = ASDU_pro(ASDU[0]) ; 
	
	if (bus->mode & IEC_BALANCE)
		balm_link_check(bus,recvpkt->linkctrl); 
	else 
		unbalm_link_check(bus,recvpkt->linkctrl); 

	if (asdupro != NULL) {
		asdupro(bus,ASDU,ASDUlen);
	}
	else {
		iec10x_warn(bus,"unrecognized type:%d\r\n",ASDU[0]);
	}
}


/**
  * @brief    定长包解析
  * @param    bus       : 总线
  * @param    data      : 包数据 
  * @return   解析成功返回 0 
*/
static void iec101_shortpkt_parse(iec10x_t *bus,uint8_t * data)
{
	int  datalen = 4 + bus->iec101.lnaddrlen;

	if (data[datalen-1] != 0x16 ) { //帧尾错误
		iec10x_err(bus,"cannot found frame tail\r\n");
		return ;
	}

	if (data[datalen-2] != calchksum(&data[1],datalen-3)){
		/* 减去帧头帧尾的和校验错误 */
		iec10x_err(bus,"check sum error\r\n");
		return ;
	}

	// 到此为校验正确的包，处理包数据
	if (!(bus->mode & IEC_BALANCE)){ 
		/* 非平衡模式数据包 */
		if (!(bus->mode & IEC_MASTER))
			unbals_link_check(bus,data[1]);
		else 
			unbalm_link_check(bus,data[1]);
	}
	else { 
		/* 平衡模式数据包 */
		if (!(bus->mode & IEC_MASTER))
			bals_link_check(bus,data[1]);
		else 
			balm_link_check(bus,data[1]);
	}
}


/**
  * @brief    解析不定长包
  * @note
  * <pre>
  * 0x68-len-len-0x68-linkctrl(1)-linkaddr(1/2)-ASDU(x)-checkcode-0x16
  *                   | <-----------len------------> |
  * 
  * ASDU : type(1)-qualifier(1)-cause(1/2)-commonaddr(1/2)-infoaddr(2)-info(x)
  * </pre>
  *  qualifier 为 可变结构限定词
  *  由于 linkaddr/cause/commonaddr 有可能为 1 或 2 个字节，所以需要做判断
  *  链路层只负责打包解包，解包得到的 ASDU 用户数据此处不管，交由协议层处理
  * @param    bus   : 需要解析的总线结构体 
  * @return   返回解析结果
*/
static void iec101_longpkt_parse(iec10x_t *bus,uint8_t * data)
{
	struct longpkt * recvpkt  = (struct longpkt*)data;
	int32_t datalen = recvpkt->len + 4 + 2 ; //总包长。加上帧头(4)帧尾(2)

	if (recvpkt->head != recvpkt->head2 || recvpkt->len2 != recvpkt->len){
		iec10x_err(bus,"frame head error\r\n");
		return ;
	}

	if (data[datalen - 1] != 0x16) {
		iec10x_err(bus,"cannot found frame tail\r\n");
		return ;
	}

	/* 减去帧头帧尾的和校验 */
	if (data[datalen-2] != calchksum(&recvpkt->linkctrl,recvpkt->len)) {
		iec10x_err(bus,"check sum error\r\n");
		return ;
	}

	/* 到此为校验正确的包，处理包数据 */
	if (!(bus->mode & IEC_MASTER)) {
		iec101slv_longpkt(bus,recvpkt); 
	}
	else { 
		iec101mst_longpkt(bus,recvpkt);
	}
}


/**
  * @brief    总线接收数据并解析
  * @param    bus   : 需要解析的总线结构体 
  * @param    data  : 包数据 
  * @param    len   : 包数据长度
  * @return   don't care
*/
void iec101_protocol_recv(iec10x_t * bus , uint8_t * data ,int len)
{
	int pktlen , need;

	/* 断包判断处理，上一次不完整的包存于 bus->rxbuf 中，长度为 bus->rxtail ，
	   把当前包 data 拼接至 bus->rxbuf 后面，如果包完整则解析，否则继续缓存 */
	while(bus->rxtail && len) {
		/* 计算当前包应有的长度 pktlen */
		if (bus->rxbuf[0] == 0x10) {
			pktlen = 4 + bus->iec101.lnaddrlen ;
		}
		else {
			pktlen = (bus->rxtail > 1) ? (6 + bus->rxbuf[1]) : (6 + data[0]);
		}

		/* 计算包仍需多少个字节才完整，如果仍不完整，缓存 data 至 bus->rxbuf */
		need = pktlen-bus->rxtail ;
		if (len < need) {
			memcpy(&bus->rxbuf[bus->rxtail],data,len);
			bus->rxtail += len ;
			return ;
		}

		/* 缓存满一包，拼接剩余内容至 bus->rxbuf 并解析 */
		memcpy(&bus->rxbuf[bus->rxtail],data,need);
		data += need;
		len  -= need;
		bus->rxtail = 0;

		/* 报文解析打印 */
		IEC101_PACKET_INFO(bus, bus->rxbuf, pktlen,IEC_RX_PKT);

		if (bus->rxbuf[0] == 0x10)
			iec101_shortpkt_parse(bus,bus->rxbuf);
		else 
			iec101_longpkt_parse(bus,bus->rxbuf);
	}
	
	/* 缓冲区 bus->rxbuf 为空，判断当前包 data 是否完整，完整则解析，否则缓存 */
	while( len ) {
		if (data[0] == 0xE5){
			bus->busy &= ~IEC101_WAITING_CONFORM ;
			data++ ;
			len--  ;
			continue ;
		}

		/* 计算当前包应有的长度 pktlen */
		if (data[0] == 0x10) {
			pktlen = 4 + bus->iec101.lnaddrlen ;
		}
		else 
		if (data[0] == 0x68) {
			pktlen  = (len < 2) ? 0xff : (6 + data[1]) ;
		}
		else {
			iec10x_err(bus,"cannot found frame head!\n");
			return ;
		}

		if (len < pktlen) {
			memcpy(bus->rxbuf,data,len);
			bus->rxtail = len;
			len = 0;
		}

		/* data 满一包，直接解析 data */
		IEC101_PACKET_INFO(bus,data, pktlen,IEC_RX_PKT);

		if (data[0] == 0x10)
			iec101_shortpkt_parse(bus,data);
		else
			iec101_longpkt_parse(bus,data); 
		data += pktlen ;
		len  -= pktlen ;
	}
}




/**
  * @brief    总线对需要发送的 ASDU 数据进行打包，调用硬件发送
  * @param    bus   : 需要解析的总线结构体 
  * @param    data  : ASDU 数据 
  * @param    len   : ASDU 数据长度 
  * @param    lnkctrl   : ASDU 数据所需控制域
  * @return   don't care
*/
void iec101_protocol_send(iec10x_t * bus , uint8_t * ASDU ,uint16_t ASDUlen ,uint8_t lnkctrl)
{
	uint8_t   chksum = 0xE5;
	uint8_t * buff = bus->txbuf;
	uint16_t  addr = bus->iec101.lnaddr ;

	if (ASDU != NULL && ASDUlen ) {
		struct longpkt * txpkt = (struct longpkt *)bus->txbuf;
		txpkt->head        = 0x68;
		txpkt->len         = ASDUlen + bus->iec101.lnaddrlen + 1;
		txpkt->len2        = txpkt->len ;
		txpkt->head2       = 0x68;
		txpkt->linkctrl    = lnkctrl;
		txpkt->linkaddr[0] = addr & 0x00ff ;
		if (bus->iec101.lnaddrlen > 1) {
			txpkt->linkaddr[1] = (addr >> 8) & 0x00ff ;
		}
		
		buff = &txpkt->linkaddr[bus->iec101.lnaddrlen]; //ASDU 信息位置
		if (ASDU != buff) {
			memcpy(buff,ASDU,ASDUlen);
		}
		chksum = calchksum(&txpkt->linkctrl,ASDUlen+1+bus->iec101.lnaddrlen);

		buff   += ASDUlen ; //得到发送包结尾处
		*buff++ = chksum ;
		*buff++ = 0x16 ;

		if (bus->mode & (IEC_MASTER|IEC_BALANCE))
			bus->busy |= IEC101_WAITING_CONFORM ;
	}
	else {
		/* 无 ASDU 数据，以固定包格式组包发送 */
		*buff++ = 0x10;
		*buff++ = lnkctrl ;
		*buff++ = addr & 0x00ff ;
		if (bus->iec101.lnaddrlen > 1) {
			*buff++ = (addr >> 8) & 0x00ff ;
		}
		chksum = calchksum(&bus->txbuf[1],buff - (&bus->txbuf[1]));
		*buff++ = chksum ;
		*buff++ = 0x16 ;

		if ((bus->mode & (IEC_MASTER|IEC_BALANCE)) && (lnkctrl & 0x40)) {
			/* 如果是平衡模式且为主导发送方，标记状态量 */
			bus->busy |= IEC101_WAITING_CONFORM ;
		}
	}
	bus->onlinet = iec_timestamp ;//记录时间戳
	bus->haltx(bus->txbuf,buff - bus->txbuf);
	
	/* 报文解析打印 */
	IEC101_PACKET_INFO(bus,bus->txbuf,buff-bus->txbuf,IEC_TX_PKT);
}



/**
  * @author   古么宁
  * @brief    发送重连接请求
  * @param    bus : 需要连接的总线
  * @return   
*/
void iec101mst_conncet(iec10x_t * bus)
{
	uint8_t  lnkctrl = IEC_CTRL(0,1,0,0,MASTER_LNK_STAT) ;

	iec101_disconnect(bus);

	iec10x_msg(bus,"request connect\r\n");
	
	if (bus->mode & IEC_BALANCE) 
		lnkctrl |= (bus->mode & IEC_DIRECT);

	iec101_protocol_send(bus,NULL,0,lnkctrl);
	bus->busy |= IEC101_WAITING_CONFORM ; 
}


/**
  * @author   古么宁
  * @brief    发送链路测试心跳包
  * @param    bus : 对象总线
  * @return   
*/
void iec101_heartbeat(iec10x_t * bus)
{
	int FCB = bus->iec101.FCB == 0;
	uint8_t  lnkctrl ;
	lnkctrl  = IEC_CTRL(0,1,FCB,1,MASTER_TEST_LNK) ;
	lnkctrl |= (bus->mode & IEC_DIRECT) ;
	bus->iec101.FCB = FCB;
	bus->busy |= IEC101_WAITING_CONFORM ; 
	iec101_protocol_send(bus,NULL,0,lnkctrl);
}


void iec101_connected(iec10x_t *bus)
{
	iec10x_msg(bus,"iec bus connected!!\r\n");
	bus->connected = 1;
	bus->ctrling   = 0;
	bus->periodt   = iec_timestamp - bus->data_period + 1000;
	bus->synct     = iec_timestamp - bus->sync_period + 1000;
}

/**
  * @author   古么宁
  * @brief    断开连接
  * @param    bus : 对象总线
  * @return   
*/
void iec101_disconnect(iec10x_t * bus)
{
	bus->connected  = IEC_DISCONNECTED  ;
	bus->iec101.FCB = 0                 ;
	bus->ctrling    = 0                 ;
	bus->busy       = 0                 ;
	memset(&bus->operate,0,sizeof(struct iec_do));
	memset(&bus->ctrl   ,0,sizeof(struct iec_do));

	iec10x_event_deinit(bus);
}



#else /** (ENABLE_IEC101_MODE) */

void iec101_protocol_recv(iec10x_t * bus , uint8_t * data ,int len)
{
	iec10x_warn(bus,"iec101 is unable.\r\n");
	iec10x_warn(bus,"please check \"iec_sys.h\" : \r\n");
	iec10x_warn(bus,"ENABLE_IEC101_MASTER or ENABLE_IEC101_SLAVE \r\n");
}

void iec101_protocol_send(iec10x_t * bus , uint8_t * ASDU ,uint16_t ASDUlen ,uint8_t lnkctrl)
{
	iec101_protocol_recv(0,0,0);
}

#endif
